<?php
	
	namespace org\tekuna\framework\action;
	
	use org\tekuna\core\context\Context;
	use org\tekuna\core\event\Event;
	use org\tekuna\core\configuration\ConfigurationElement;
	
	use org\tekuna\framework\filter\Filter;
	use org\tekuna\framework\filter\data\NoSpecialCharsFilter;
	use org\tekuna\framework\request\Request;
	
	
	/**
	 * This ActionEvent is triggered when an Action is matched within a
	 * HTTP request.
	 */
	class HttpActionEvent extends ActionEvent {

		protected
			$sMatchedComponentBaseUrl = NULL,
			$sMatchedActionUrl = NULL,
			$sMatchedPattern = NULL,
			$sMatchedRegex = NULL,
			$arrMatchedUrlParts = array(),
			$objRequest = NULL,

			$objDefaultFilter = NULL,
			$arrPartFilters = array();


		/**
		 * Construct a new HttpActionEvent
		 * 
		 * @param Context $objContext the application context
		 * @param ConfigurationElement $objComponentElement the configuration 
		 *        element of the action's component
		 * @param ConfigurationElement $objActionElement the configuration 
		 *        element of the action
		 */
		public function __construct(Context $objContext, ConfigurationElement $objComponentElement, ConfigurationElement $objActionElement) {
			
			parent :: __construct($objContext, $objComponentElement, $objActionElement);
			$this -> objRequest = $objContext -> getRequest();
			$this -> objDefaultFilter = new NoSpecialCharsFilter();
		}
		
		public function setMatchedUrlParts($arrMatchedUrlParts) {
			
			$this -> arrMatchedUrlParts = $arrMatchedUrlParts;
		}
		
		public function setMatchedComponentBaseUrl($sMatchedComponentBaseUrl) {
			
			$this -> sMatchedComponentBaseUrl = $sMatchedComponentBaseUrl;
		}
		
		public function getMatchedComponentBaseUrl() {
			
			return $this -> sMatchedComponentBaseUrl;
		}
		
		public function setMatchedActionUrl($sMatchedActionUrl) {
			
			$this -> sMatchedActionUrl = $sMatchedActionUrl;
		}

		public function getMatchedActionUrl() {
			
			return $this -> sMatchedActionUrl;
		}
		
		public function setMatchedPattern($sMatchedPattern) {
			
			$this -> sMatchedPattern = $sMatchedPattern;
		}
		
		public function getMatchedPattern() {
			
			return $this -> sMatchedPattern;
		}
		
		public function setMatchedRegex($sMatchedRegex) {
			
			$this -> sMatchedRegex = $sMatchedRegex;
		}

		public function getMatchedRegex() {
			
			return $this -> sMatchedRegex;
		}
		
		public function getMatchedComponentActionUrl() {
			
			return $this -> sMatchedComponentBaseUrl . $this -> sMatchedActionUrl;
		}
		
		/**
		 * Set the default filter that is applied, if no special filter
		 * for a specific matched URL part is set.
		 *
		 * ATTENTION: Use this method only as last possibility. For security
		 * reasons it is always better to use the setPartFilter() method
		 * to set individual filters for each url part.
		 *
		 * @param Filter $objFilter the new filter object
		 */
		public function setDefaultFilter(Filter $objFilter) {

			$this -> objDefaultFilter = $objFilter;
		}


		/**
		 * @return Filter returns the current active default filter
		 */
		public function getDefaultFilter() {

			return $this -> objDefaultFilter;
		}


		/**
		 * Set a filter object for a certain matched URL part.
		 *
		 * @param string $sUrlPart the URL part
		 * @param Filter a filter object
		 */
		public function setPartFilter($sUrlPart, Filter $objFilter) {

			$this -> arrPartFilters[$sUrlPart] = $objFilter;
		}


		/**
		 * Returns the filter for a specific matched URL part. If
		 * no special filter is set (and implicitly the default filter
		 * would be applied) this method returns NULL.
		 *
		 * @param string $sUrlPart the URL part
		 * @return Filter object or NULL
		 */
		public function getPartFilter($sUrlPart) {

			if (isset($this -> arrPartFilters[$sUrlPart])) {

				return $this -> arrPartFilters[$sUrlPart];
			}
			else {

				return NULL;
			}
		}


		/**
		 * Returns the value of a mapped part from URL. If the value is not available
		 * a RequestException is thrown. The defined filter (or the default filter)
		 * is applied to the value and (if it is an array) all values in any depth.
		 *
		 * @param string $sField the field in the GET scope
		 * @return string the value of this field
		 */
		public function getUrlPart($sUrlPart) {

			if (! isset($this -> arrMatchedUrlParts[$sUrlPart])) {

				throw new ActionException("The URL part '$sUrlPart' is not available.");
			}

			$objFilter = NULL;
			if (isset($this -> arrPartFilters[$sUrlPart])) {

				$objFilter = $this -> arrPartFilters[$sUrlPart];
			}
			else {

				$objFilter = $this -> objDefaultFilter;
			}

			return $this -> objRequest -> applyFilterRecursive($this -> arrMatchedUrlParts[$sUrlPart], $objFilter);
		}


		/**
		 * @return array returns an associative array of all available URL parts.
		 */
		public function getAllUrlParts() {

			$arrReturn = array();

			foreach (array_keys($this -> arrMatchedUrlParts) as $sUrlPart) {

				$arrReturn[$sUrlPart] = $this -> getUrlPart($sUrlPart);
			}

			return $arrReturn;
		}
		
		
		public function buildActionUrl($sActionPath) {
			
			// get the URL for the current request
			$objUrl = $this -> objRequest -> buildCurrentRequestUrl();
			
			// adjust the path
			$sNewPath = $objUrl -> getPath();
			$sNewPath = substr($sNewPath, 0, -strlen($this -> getMatchedComponentActionUrl()));
			$sNewPath .= $sActionPath;
			$objUrl -> setPath($sNewPath);
			
			// remove all parameters
			$objUrl -> removeParameters();
			
			return $objUrl;
		}
		
		
		/**
		 * provide some internal information about the matched action
		 * to the ApplicationInfoListener.
		 */
		public function handleEvent(Event $objEvent) {
			
			$sOutput = '';
			$sOutput .= 'Action Event: '. get_class($this) ."\n";
			$sOutput .= 'Action Class: '. get_class($this -> getAction()) ."\n";
			
			$sOutput .= "\nMatched URL Parts\n";
			$sOutput .= "-----------------\n";
			foreach ($this -> getAllUrlParts() as $sUrlPart => $sValue) {

				$objFilter = $this -> getPartFilter($sUrlPart);
				if ($objFilter === null) {

					$sFilterClass = '*';
				}
				else {

					$sFilterClass = get_class($objFilter);
				}

				$sOutput .= '   '. sprintf('%-40s', $sUrlPart .' = '. $sValue) ."($sFilterClass)\n";
			}
			$sOutput .= "\n* Default Filter = ". get_class($this -> getDefaultFilter()) ."\n\n";
			
			$sOutput .= 'Matched Component URL: '. $this -> getMatchedComponentBaseUrl() ."\n";
			$sOutput .= 'Matched Action URL: '. $this -> getMatchedActionUrl() ."\n";
			$sOutput .= 'Used Pattern: '. $this -> getMatchedPattern() ."\n";
			$sOutput .= 'Used Regex: '. $this -> getMatchedRegex() ."\n\n";
		
			$sOutput .= "Matched Component:\n";
			$sOutput .= $this -> getComponentElement() -> __toString() ."\n\n";
						
			$sOutput .= "Matched Action:\n";
			$sOutput .= $this -> getActionElement() -> __toString() ."\n\n";
			
			// add new info section
			$objEvent -> addInfoSection('HTTP Action Information', $sOutput);
		}
	}
